package com.tanhua.server.service;

import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.tanhua.commons.template.OssTemplate;
import com.tanhua.domain.db.UserInfo;
import com.tanhua.domain.mongo.Comment;
import com.tanhua.domain.mongo.Video;
import com.tanhua.domain.vo.CommentVO;
import com.tanhua.domain.vo.PageResult;
import com.tanhua.domain.vo.VideoVO;
import com.tanhua.dubbo.api.CommentApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.VideoApi;
import com.tanhua.server.interceptor.UserHolder;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author anshuai
 * @create 2021-02-02 16:18
 */
@Service
public class VideoService {

    @Reference
    private VideoApi videoApi;
    @Reference
    private UserInfoApi userInfoApi;
    @Reference
    private CommentApi commentApi;
    @Autowired
    private OssTemplate ossTemplate;
    @Autowired
    private FastFileStorageClient fastFileStorageClient;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private FdfsWebServer fdfsWebServer;
    @Autowired
    private VideoMQService mqService;

    /**
     * 小视频-发布视频
     *
     * @param videoThumbnail 视频封面文件
     * @param videoFile      视频文件
     * @return
     */
    //allEntries = true表示清除的是以video_list开头的所有缓存数据
    //allEntries = false表示清除的是指定key的缓存数据
    // @CacheEvict(cacheNames = "video_list", allEntries = true)
    public ResponseEntity saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
        //1. 把图片存储到阿里云OSS
        String imgPath = ossTemplate.upload(videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream());

        //2. 把视频存储到FastDFS
        //获取文件后缀名
        //   xxxx.mp4
        String originalFilename = videoFile.getOriginalFilename();
        //   mp4
        String suffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);

        //上传到fastdfs服务器上需要三个参数:1.文件输入流、2.文件大小、3.文件后缀名
        StorePath storePath = fastFileStorageClient.uploadFile(videoFile.getInputStream(), videoFile.getSize(), suffix, null);
        //获取文件的存储路径
        String fullPath = storePath.getFullPath();
        //拼接文件访问路径
        //fdfsWebServer.getWebServerUrl():获取配置文件中的访问路径
        String videoPath = fdfsWebServer.getWebServerUrl() + fullPath;

        //3. 把视频信息存储到MongoDB
        Video video = new Video();
        video.setPicUrl(imgPath);
        video.setVideoUrl(videoPath);
        video.setText("未来可期~");
        video.setUserId(UserHolder.getUserId());

        String videoId = videoApi.save(video);

        //发送消息，供大数据推荐系统使用
        mqService.saveVideoMessage(videoId);

        return ResponseEntity.ok(null);
    }

    /**
     * 查询小视频列表
     */
    //方法上加Cacheable注解,方法返回值会被缓存，缓存的键是 `cacheNames::key
    //spring缓存数据时需要数据实现序列化接口
    // @Cacheable(cacheNames = "video_list", key = "#page+'_'+#pagesize")
    public PageResult<VideoVO> findVideoList(int page, int pagesize) {
        //1.查询视频列表 优先从Redis里找推荐的视频；如果找不到再从MongoDB里查找
        PageResult<Video> pageResult = findVideoFromRedis(page, pagesize);
        if (pageResult == null) {
            pageResult = videoApi.findVideoList(page, pagesize, UserHolder.getUserId());
        }

        //2.转换VO
        List<Video> videoList = pageResult.getItems();
        List<VideoVO> voList = new ArrayList<>();
        for (Video video : videoList) {
            VideoVO vo = new VideoVO();

            //封装视频发布者的信息
            UserInfo userInfo = userInfoApi.findById(video.getUserId());
            vo.setAvatar(userInfo.getAvatar());
            vo.setNickname(userInfo.getNickname());
            vo.setUserId(userInfo.getId());

            //封装视频信息
            vo.setId(video.getId().toHexString());
            vo.setCover(video.getPicUrl());
            vo.setSignature(video.getText());
            vo.setVideoUrl(video.getVideoUrl());

            vo.setLikeCount(video.getLikeCount());
            vo.setCommentCount(video.getCommentCount());

            //当前用户对此视频是否点赞了
            Boolean hasLike = redisTemplate.hasKey("video_like_" + UserHolder.getUserId() + "_" + video.getUserId());
            vo.setHasLiked(hasLike ? 1 : 0);
            //当前用户对此视频作者是否关注了
            Boolean hasFocus = redisTemplate.hasKey("video_focus_" + UserHolder.getUserId() + "_" + video.getUserId());
            vo.setHasFocus(hasFocus ? 1 : 0);

            voList.add(vo);
        }

        //3.构造返回值
        PageResult<VideoVO> result = new PageResult<>();
        BeanUtils.copyProperties(pageResult, result);
        result.setItems(voList);

        return result;
    }

    /**
     * 从redis中查询大数据推荐的小视频
     */
    private PageResult<Video> findVideoFromRedis(int page, int pagesize) {

        //1. 从Redis里查找数据 100049,100016,100002,100023,100050,23,20,17,100074,100056
        String vidStr = redisTemplate.opsForValue().get("QUANZI_VIDEO_RECOMMEND_" + UserHolder.getUserId());
        if (vidStr == null) {
            return null;
        }

        //2. 计算总数量
        String[] vidArray = vidStr.split(",");
        int count = vidArray.length;

        //3. 计算分了多少页
        int pages = (int) Math.ceil(count * 1.0 / pagesize);

        //4.查询视频列表
        if (page <= pages) {
            int start = (page - 1) * pagesize;
            int end = start + pagesize;

            if (end > count) {
                end = count;
            }

            List<Long> vidList = new ArrayList<>();
            for (int i = start; i < end; i++) {
                long vid = Long.parseLong(vidArray[i]);
                vidList.add(vid);
            }

            List<Video> videoList = videoApi.findVideoByVids(vidList);

            return new PageResult<>(count,pagesize,pages,page,videoList);
        }

        return new PageResult<>();
    }

    /**
     * 关注用户
     *
     * @param targetUserId 关注的用户的id
     * @return
     */
    public ResponseEntity userFocus(Long targetUserId) {

        //1.保存用户关注表
        videoApi.followUser(UserHolder.getUserId(), targetUserId);

        //2.将关注状态保存到redis中
        redisTemplate.opsForValue().set("video_focus_" + UserHolder.getUserId() + "_" + targetUserId, "1");

        //3.返回结果
        return ResponseEntity.ok(null);
    }

    /**
     * 取消关注
     *
     * @param targetUserId 被关注用户的id
     * @return
     */
    public ResponseEntity userUnFocus(Long targetUserId) {

        //1.移除用户关注关系
        videoApi.disFollowUser(UserHolder.getUserId(), targetUserId);

        //2.将保存在redis中的关注关系删除
        redisTemplate.delete("video_focus_" + UserHolder.getUserId() + "_" + targetUserId);

        //3.返回结果
        return ResponseEntity.ok(null);
    }

    /**
     * 视频-点赞
     *
     * @param videoId 视频id
     * @return
     */
    public ResponseEntity likeVideo(String videoId) {
        //1.获取当前用户id
        Long userId = UserHolder.getUserId();

        //2.封装数据Comment对象
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(videoId));
        //设置评论类型为点赞
        comment.setCommentType(1);
        //设置评论内容类型为视频
        comment.setPubType(2);
        comment.setUserId(userId);

        //设置被评论的用户
        Video video = videoApi.findById(videoId);
        comment.setPublishUserId(video.getUserId());

        //保存评论数据
        long count = commentApi.saveVideo(comment);

        //4.把点赞记录保存到redis
        redisTemplate.opsForValue().set("video_like_" + userId + "_" + video.getUserId(), "1");

        //发送消息，供大数据推荐系统使用
        mqService.likeVideoMessage(videoId);

        return ResponseEntity.ok(count);

    }

    /**
     * 取消视频点赞
     *
     * @param videoId 视频点赞
     * @return
     */
    public ResponseEntity disLikeVideo(String videoId) {
        //1.获取当前用户id
        Long userId = UserHolder.getUserId();

        //2.封装条件数据
        Comment comment = new Comment();
        comment.setUserId(userId);
        comment.setPublishId(new ObjectId(videoId));
        comment.setCommentType(1);

        //3.调用Api删除数据
        long count = commentApi.removeVideo(comment);

        //查询视频用户id,作为redis的key值用
        Video video = videoApi.findById(videoId);

        //4.从redis中删除之前的点赞记录
        redisTemplate.delete("video_like_" + userId + "_" + video.getUserId());

        //发送消息，供大数据推荐系统使用
        mqService.unlikeVideoMessage(videoId);

        return ResponseEntity.ok(count);
    }

    /**
     * 视频-查询评论列表
     *
     * @param videoId  视频id
     * @param page     当前页码
     * @param pagesize 每页几条
     */
    public ResponseEntity findComments(String videoId, int page, int pagesize) {
        //1.查询视频的评论列表
        PageResult<Comment> pageResult = commentApi.findCommentList(videoId, page, pagesize);

        //2.转换VO
        List<Comment> commentList = pageResult.getItems();
        List<CommentVO> voList = new ArrayList<>();
        for (Comment comment : commentList) {
            CommentVO vo = new CommentVO();

            //将评论对象的数据封装到vo
            BeanUtils.copyProperties(comment, vo);
            vo.setId(comment.getId().toHexString());

            String dateStr = new SimpleDateFormat("yyyy年MM月dd日 HH:mm").format(new Date(comment.getCreated()));
            vo.setCreateDate(dateStr);

            //把评论者的数据封装到vo
            UserInfo userInfo = userInfoApi.findById(comment.getUserId());
            BeanUtils.copyProperties(userInfo, vo);

            //当前用户是否对这条评论点赞了
            Boolean hasLike = redisTemplate.hasKey("video_comment_like_" + UserHolder.getUserId() + "_" + comment.getId().toHexString());
            vo.setHasLiked(hasLike ? 1 : 0);

            voList.add(vo);
        }

        //3.构造返回值
        PageResult<CommentVO> result = new PageResult<>();
        BeanUtils.copyProperties(pageResult, result);
        result.setItems(voList);

        return ResponseEntity.ok(result);
    }

    /**
     * 视频-发表评论
     *
     * @param videoId 视频id
     * @param content 评论内容
     */
    public ResponseEntity saveComments(String videoId, String content) {

        //1.补全Comment对象数据
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(videoId));
        comment.setCommentType(2);
        comment.setUserId(UserHolder.getUserId());
        comment.setPubType(2);
        comment.setContent(content);

        //设置被评论的用户
        Video video = videoApi.findById(videoId);
        comment.setPublishUserId(video.getUserId());

        //保存评论对象数据
        commentApi.saveVideo(comment);

        //发送消息，供大数据推荐系统使用
        mqService.commentVideoMessage(videoId);

        return ResponseEntity.ok(null);
    }

    /**
     * 视频评论点赞
     * @param commentId 评论id
     * @return
     */
    /*public ResponseEntity likeComment(String commentId) {

        //1.获取当前用户
        Long userId = UserHolder.getUserId();

        //2.封装数据Comment对象
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(commentId));
        //设置评论对象数据类型为点赞
        comment.setCommentType(1);
        //设置对评论点赞
        comment.setPubType(3);
        comment.setUserId(userId);

        //设置被评论的用户
        Comment commentUser = commentApi.findById(commentId);
        comment.setPublishUserId(commentUser.getPublishUserId());

        //3.保存被评论的用户
        long count = commentApi.saveComment(comment);

        //4.把点赞记录存储到redis
        redisTemplate.opsForValue().set("video_comment_like_" + userId + "_" + commentUser.getId().toHexString(),"1");

        return ResponseEntity.ok(count);
    }*/

    /**
     * 取消视频评论点赞
     * @param commentId 评论id
     * @return
     */
    /*public ResponseEntity disLikeComment(String commentId) {

        //1.准备条件
        Comment comment = new Comment();
        comment.setUserId(UserHolder.getUserId());
        comment.setCommentType(1);
        comment.setPublishId(new ObjectId(commentId));

        //2.调用Api,删除Comment数据
        long count = commentApi.removeComment(comment);

        //3.删除redis的点赞状态
        Comment commentUser = commentApi.findById(commentId);
        redisTemplate.delete("video_comment_like_" + UserHolder.getUserId() + "_" + commentUser.getId().toHexString());

        return ResponseEntity.ok(count);
    }*/
}
